#pragma once
#ifndef OBR_RENDERER_H
#define OBR_RENDERER_H

#define __uninitialized_deviceptr 0

#include "Background.h"
#include "Camera.h"
#include "Instance.h"
#include "Integrator.h"
#include "Light.h"
#include "Material.h"
#include "Mesh.h"
#include "SharedDS/LaunchDS.h"
#include "SharedDS/Records.h"
#include "VirtualProgramManager.h"
#include "common.h"

namespace OBR
{
class Renderer
{
    // data
public:
    OptixDeviceContext context; // context
    CUstream stream;            // cuda stream
    CudaBuffer d_fbos;          // results

private:
    OptixPipeline pipeline;
    OptixShaderBindingTable sbt;
    CudaBuffer d_launch_params;
    CudaBuffer d_miss_sbt_record, d_raygen_sbt_record, d_hit_sbt_records, d_callable_sbt_records;

    // methods
public:
    Renderer();
    ~Renderer();

    /**
     * @brief render one frame using base params
     *
     * @param base_params
     * @return Params
     */
    void renderFrame(Params &params);

    /**
     * @brief create an Optix Pipeline and SBT
     *
     * @param pipeline_compile_options
     */
    void createPipeline(OptixPipelineCompileOptions &pipeline_compile_options);

    /**
     * @brief Create shader binding table, note that hit records are created using public static
     * members of Instance class
     *
     * @param camera
     * @param background
     * @param integrator
     * @param unique_lights
     * @param unique_materials
     * @param unique_mediums
     */
    void createSbt(std::shared_ptr<Camera> &camera,         // raygen
                   std::shared_ptr<Background> &background, // miss,
                   VirtualProgramManager &mesh_manager,     // hit
                   std::shared_ptr<Integrator> &integrator, // callable
                   VirtualProgramManager &light_manager,    // callable
                   VirtualProgramManager &material_manager, // callable
                   VirtualProgramManager &medium_manager    // callable
    );

private:
    void initOptix();
    void freeResources();
    void prepareLaunchParams(Params &base);
    void downloadResult(Params &dest);
    void setRaygenSbt(std::shared_ptr<Camera> &cam, uint med_id_offset);
    void setMissSbt(std::shared_ptr<Background> &bg);
    void setHitSbt(uint n_lights, uint n_mat, VirtualProgramManager &mesh_manager);
    void setCallableSbt(std::shared_ptr<Integrator> &integrator,
                        VirtualProgramManager &light_manager,
                        VirtualProgramManager &material_manager,
                        VirtualProgramManager &medium_manager);
};
} // namespace OBR

#endif